//////////////////////////////////////////////////////////////////////////
// $Id: MThread.cpp,v 1.1 2009/04/03 12:46:10 sunjun Exp $
//////////////////////////////////////////////////////////////////////////

#include "Local.h"
#include <process.h>

//////////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////////

NAMESPACE_BEGIN

//////////////////////////////////////////////////////////////////////////

#ifdef _WIN32

	//////////////////////////////////////////////////////////////////////////

	MThread::MThread() 
	{
		GUARD(MThread::MThread);

		mHandle = NULL;
		mRunning = FALSE;

		UNGUARD;
	}

	MThread::~MThread() 
	{
		GUARD(MThread::~MThread);
		UNGUARD;
	}

	/*
	====================
	start
	====================
	*/
	INLINE VOID MThread::start( U32 size, U32 priority )
	{
		GUARD(MThread::start);

		// keep this alive for the duration of start
		MThreadPtr keep_me = this;

		// lock the thread data
		MLock lock(&mMutex);

		// It's necessary to increment the reference count since
		// pthread_create won't necessarily call the thread function until
		// later. If the user does (new MyThread)->start() then the thread
		// object could be deleted before the thread object takes
		// ownership. It's also necessary to increment the reference count
		// prior to calling pthread_create since the thread itself calls
		// unref().
		ref();

		// create the thread
#ifdef _WIN32_WCE
		mHandle = ::CreateThread( 0, size, hook, this, CREATE_SUSPENDED, &mID );
#else
		U32 id;
		mHandle = reinterpret_cast<HANDLE>(::_beginthreadex(0, size, hook, this, CREATE_SUSPENDED, &id));
#endif
		if( mHandle == 0 )
		{
			unref();
			MAssert(VA("Fail to create a new thread and the error code is %d!", ::GetLastError()));
		}

		// set the thread priority
		I32 prio = THREAD_PRIORITY_NORMAL;
		switch( priority ) 
		{
		case THREAD_PRIORITY_MAX:
			prio = THREAD_PRIORITY_HIGHEST;
			break;
		case THREAD_PRIORITY_HIGH:
			prio = THREAD_PRIORITY_ABOVE_NORMAL;
			break;
		case THREAD_PRIORITY_NOMINAL:
			prio = THREAD_PRIORITY_NORMAL;
			break;
		case THREAD_PRIORITY_LOW:
			prio = THREAD_PRIORITY_BELOW_NORMAL;
			break;
		case THREAD_PRIORITY_MIN:
			prio = THREAD_PRIORITY_IDLE;
			break;
		}
#ifdef _WIN32_WCE
		if(::CeSetThreadPriority(mHandle, priority) == 0)
		{
			unref();
			MAssert(VA("Fail to set the thread priority and the error code is %d!", ::GetLastError()));
		}
#else
		if(::SetThreadPriority(mHandle, priority) == 0)
		{
			unref();
			MAssert(VA("Fail to set the thread priority and the error code is %d!", ::GetLastError()));
		}
#endif

		// resume the thread
		if(::ResumeThread(mHandle) == -1)
		{
			unref();
			MAssert(VA("Fail to resume the thread and the error code is %d!", ::GetLastError()));
		}

		// set the thread is running
		mRunning = TRUE;

		UNGUARD;
	}

	/*
	====================
	end
	====================
	*/
	INLINE VOID MThread::end()
	{
		GUARD(MThread::end);

		// set the thread is done
		done();

		// terminate the thread and close the thread handle
		if( mHandle )
		{
			// wait until the controlled thread terminates.
			if( ::WaitForSingleObject(mHandle, INFINITE) != WAIT_OBJECT_0 )
			{
				MAssert( VA("Fail to terminate the thread and the error code is %d!", ::GetLastError()) );
			}

			// close the thread handle
			if( ::CloseHandle(mHandle) == 0 )
			{
				MAssert( VA("Fail to close the thread handle and the error code is %d!", ::GetLastError()) );
			}

			mHandle = NULL;
		}

		UNGUARD;
	}

	/*
	====================
	hook
	====================
	*/
	U32 STDCALL MThread::hook( VOID * arg ) 
	{
		GUARD(MThread::hook);

		// ensure that the thread doesn't go away until run() has completed.
		MThreadPtr thread_ptr;

		try
		{
			// get the thread itself
			MThread * raw_thread = static_cast<MThread*>(arg);

			// ensure that the thread doesn't go away until run() has completed.			
			thread_ptr = raw_thread;

			// the thread unreferenced itself.
			raw_thread->unref();

			// the thread run
			thread_ptr->run();
		}
		catch (...)
		{
			MAssert("The thread run is error!");
		}

		return 0;

		UNGUARD;
	}

	/*
	====================
	alive
	====================
	*/
	INLINE BOOL MThread::alive() const
	{
		GUARD(MThread::alive);

		MLock lock(&mMutex);
		return mRunning;

		UNGUARD;
	}

	/*
	====================
	done
	====================
	*/
	INLINE VOID MThread::done()
	{
		GUARD(MThread::done);

		MLock lock(&mMutex);
		mRunning = FALSE;

		UNGUARD;
	}

	/*
	====================
	suspend
	====================
	*/
	INLINE BOOL MThread::suspend() const
	{
		GUARD(MThread::suspend);

		MLock lock(&mMutex);
		if( mRunning == FALSE ) return FALSE;

		// suspend the thread
		if(::SuspendThread(mHandle) == -1)
		{
			MAssert(VA("Fail to suspend the thread and the error code is %d!", ::GetLastError()));
			return FALSE;
		}

		return TRUE;

		UNGUARD;
	}

	/*
	====================
	resume
	====================
	*/
	INLINE BOOL MThread::resume() const
	{
		GUARD(MThread::resume);

		MLock lock(&mMutex);
		if( mRunning == FALSE ) return FALSE;

		// resume the thread
		if(::ResumeThread(mHandle) == -1)
		{
			MAssert(VA("Fail to resume the thread and the error code is %d!", ::GetLastError()));
			return FALSE;
		}

		return TRUE;

		UNGUARD;
	}
	
#endif

//////////////////////////////////////////////////////////////////////////

NAMESPACE_END

//////////////////////////////////////////////////////////////////////////
